﻿using System.Diagnostics;
using System.Linq;
using Equinoxe.EZNFC.ACR122.Library;
using Equinoxe.EZNFC.Library;
using Equinoxe.EZNFC.Library.Tags;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Equinoxe.EZNFC.UnitTests
{
    [TestClass]
    public class Mifare1KTests
    {
        private const Ports _good_port = Ports.COM3;

        private const string _key_as_string = "FFFFFFFFFFFF";
        private readonly byte[] _keyAByte = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };

        private const string _keybad = "111111111111";

        private const int _block = 10;

        [TestInitialize]
        public void TestInit()
        {
            Trace.Listeners.Add(new ConsoleTraceListener());
        }

        [TestCleanup]
        public void TestClean()
        {
            Trace.Listeners.Clear();
        }


        #region Tag

        [TestMethod]
        public void AuthenticateKeyAStringTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                Assert.IsNotNull(device);

                device.Open();

                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);

                device.Close();



            }
        }

        [TestMethod]
        public void AuthenticateKeyAByteTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                Assert.IsNotNull(device);

                device.Open();



                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _keyAByte, _keyAByte.Length);



                device.Close();



            }
        }

        [TestMethod]
        public void AuthenticateBadKeyStringTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                Assert.IsNotNull(device);

                device.Open();



                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _keybad);

                device.Close();



            }
        }

        #endregion

        #region Read/Write blocks

        [TestMethod]
        public void ReadBlockTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                Assert.IsNotNull(device);

                device.Open();

                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);

                var buffer = new byte[255];

                var bufferlength = buffer.Length;

                tag.ReadBlock(_block, buffer, ref bufferlength);

                device.Close();



            }
        }

        [TestMethod]
        public void WriteBlockTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                Assert.IsNotNull(device);

                device.Open();



                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);



                var writebuffer = new byte[255];
                writebuffer[0] = 2;

                var writebufferlength = writebuffer.Length;

                tag.WriteBlock(_block, writebuffer, ref writebufferlength);

                var readbuffer = new byte[255];

                var readbufferlength = readbuffer.Length;

                tag.ReadBlock(_block, readbuffer, ref readbufferlength);

                for (var i = 0; i < readbufferlength; i++)
                {
                    Assert.AreEqual(writebuffer[i], readbuffer[i + 3]);
                }


                device.Close();



            }
        }

        [TestMethod]
        public void WriteStringTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                Assert.IsNotNull(device);

                device.Open();

                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);

                const string writestring = "01234567";

                tag.WriteBlockString(_block, writestring);

                var readstring = tag.ReadBlockString(_block);

                Assert.AreEqual(writestring, readstring);

                device.Close();

            }
        }

        #endregion

        [TestMethod]
        public void LogoffTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                Assert.IsNotNull(device);

                device.Open();

                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);

                tag.Logoff();

                device.Close();
            }
        }

        #region Value Block

        [TestMethod]
        public void WriteValueBlockTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                const int valuetowrite = 100;

                Assert.IsNotNull(device);

                device.Open();



                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);

                tag.WriteValueBlock(_block, valuetowrite, 1);

                var readvalue = tag.ReadValueBlock(_block, true);

                Assert.AreEqual(valuetowrite, readvalue);

                device.Close();
            }

        }

        [TestMethod]
        public void IncrementBlockCommitTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                const int valuetowrite = 100;

                const int incrementvalue = 1;

                Assert.IsNotNull(device);

                device.Open();



                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);

                tag.WriteValueBlock(_block, valuetowrite, _block);

                // ReSharper disable RedundantArgumentDefaultValue
                tag.IncrementValueBlock(_block, incrementvalue);
                // ReSharper restore RedundantArgumentDefaultValue

                tag.CommitBlockOperation(_block);

                var readvalue = tag.ReadValueBlock(_block, true);

                Assert.AreEqual(valuetowrite + incrementvalue, readvalue);

                device.Close();

            }
        }

        [TestMethod]
        public void IncrementBlockRollbackTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                const int valuetowrite = 100;

                const int incrementvalue = 1;

                Assert.IsNotNull(device);

                device.Open();



                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);



                tag.WriteValueBlock(_block, valuetowrite, 1);

                // ReSharper disable RedundantArgumentDefaultValue
                tag.IncrementValueBlock(_block, incrementvalue);
                // ReSharper restore RedundantArgumentDefaultValue

                tag.RollbackBlockOperation(_block);

                var readvalue = tag.ReadValueBlock(_block, true);

                Assert.AreEqual(valuetowrite, readvalue);

                device.Close();
            }
        }


        [TestMethod]
        public void DecrementBlockCommitTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                const int valuetowrite = 100;

                const int decrementvalue = 1;

                Assert.IsNotNull(device);

                device.Open();



                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);

                tag.WriteValueBlock(_block, valuetowrite, _block);

                // ReSharper disable RedundantArgumentDefaultValue
                tag.DecrementValueBlock(_block, decrementvalue);
                // ReSharper restore RedundantArgumentDefaultValue

                tag.CommitBlockOperation(_block);

                var readvalue = tag.ReadValueBlock(_block, true);

                Assert.AreEqual(valuetowrite - decrementvalue, readvalue);

                device.Close();
            }
        }

        [TestMethod]
        public void DecrementBlockRollbackTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                const int valuetowrite = 100;

                const int decrementvalue = 1;

                Assert.IsNotNull(device);

                device.Open();



                var timeouts = new ACR122Timeouts
                                   {
                                       NumberOfResponseRetry = 2,
                                       NumberOfStatusRetry = 3,
                                       ResponseTimeout = 2,
                                       StatusTimeout = 4
                                   };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);



                tag.WriteValueBlock(_block, valuetowrite, 1);

                // ReSharper disable RedundantArgumentDefaultValue
                tag.DecrementValueBlock(_block, decrementvalue);
                // ReSharper restore RedundantArgumentDefaultValue

                tag.RollbackBlockOperation(_block);

                var readvalue = tag.ReadValueBlock(_block, true);

                Assert.AreEqual(valuetowrite, readvalue);

                device.Close();



            }

        }

        [TestMethod]
        public void CopyValueBlockTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                const int valuetowrite = 66;

                Assert.IsNotNull(device);

                device.Open();



                var timeouts = new ACR122Timeouts
                {
                    NumberOfResponseRetry = 2,
                    NumberOfStatusRetry = 3,
                    ResponseTimeout = 2,
                    StatusTimeout = 4
                };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);



                tag.WriteValueBlock(_block, valuetowrite, 1);

                tag.CopyValueBlock(_block, _block + 1);

                var readvalue = tag.ReadValueBlock(_block + 1, true);

                Assert.AreEqual(valuetowrite, readvalue);

                device.Close();



            }

        }

        #endregion


        [TestMethod]
        public void BlockAccessTest()
        {
            using (var device = ACR122Device.CreateDevice(DeviceCharset.Ansi, _good_port, ACR122Device.DeviceInfoStatic.BaudsAvailable.FirstOrDefault()))
            {
                Assert.IsNotNull(device);

                device.Open();



                var timeouts = new ACR122Timeouts
                {
                    NumberOfResponseRetry = 2,
                    NumberOfStatusRetry = 3,
                    ResponseTimeout = 2,
                    StatusTimeout = 4
                };

                device.SetTimeouts(timeouts);

                var tag = device.Poll();

                Assert.IsInstanceOfType(tag, typeof(Mifare1K));

                tag.Authenticate(AuthenticatingKey.KeyA, _block, _key_as_string);



                var blockaccess = new SectorAccessConditions
                                      {
                                          Block0 = Mifare1KDataBlockAccessConditions.rAB_wAB_iAB_dtrAB,
                                          Block1 = Mifare1KDataBlockAccessConditions.rAB_wAB_iAB_dtrAB,
                                          Block2 = Mifare1KDataBlockAccessConditions.rAB_wAB_iAB_dtrAB,
                                          TrailerBlock = Mifare1KTrailerBlockAccessConditions.kawA_abrA_abwA_kbrA_kbwA
                                      };

                ((Mifare1K)tag).SetBlockAccess(_block / 4, blockaccess);

                device.Close();
            }
        }

        [TestMethod]
        public void BlockAccessFromByteArrayTest()
        {
            var buffer = new byte[] { 0, 0, 0, 0, 0, 0, 0xff, 0x7, 0x80, 0 };

            var result = SectorAccessConditions.FromByteArray(buffer);

            Assert.AreEqual(Mifare1KTrailerBlockAccessConditions.kawA_abrA_abwA_kbrA_kbwA, result.TrailerBlock);

            Assert.AreEqual(Mifare1KDataBlockAccessConditions.rAB_wAB_iAB_dtrAB, result.Block0);

            Assert.AreEqual(Mifare1KDataBlockAccessConditions.rAB_wAB_iAB_dtrAB, result.Block1);

            Assert.AreEqual(Mifare1KDataBlockAccessConditions.rAB_wAB_iAB_dtrAB, result.Block2);
        }

        //todo: block number in variable
        // todo: spelling (resul)
    }
}